import os
import re
import shutil
import sys
# import pytesseract
# import easyocr
import fitz  # PyMuPDF
import cv2
import numpy as np
from PIL import Image
import xlwings as xw
from PySide6.QtGui import *
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from win32com.client import constants

def app_path():
    if hasattr(sys, 'frozen'):  # 可执行文件走这里
        return os.path.abspath(sys.executable)
    return os.path.abspath(__file__)  # 源码走这里

class ChooseFileWidget(QWidget):
    signal_file_path = Signal(str)
    signal_worksheet_name = Signal(str)

    def __init__(self, title: str):
        super().__init__()
        self.label = QLabel(title)
        self.edit = QLineEdit()
        self.button_open = QPushButton('open')
        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.edit)
        self.layout.addWidget(self.button_open)
        self.edit.setReadOnly(True)

class ProgressBarWidget(QWidget):
    def __init__(self, title: str):
        super().__init__()
        self.label = QLabel(title)
        self.button_start = QPushButton('start')
        self.progress_bar = QProgressBar()
        self.layout = QHBoxLayout(self)
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.progress_bar)
        self.layout.addWidget(self.button_start)

class FileCheckView(QWidget):
    signal_PDF = Signal(str)
    signal_Excel = Signal(str)

    def __init__(self):
        super().__init__()
        self.workbook_event = None

        self.choosePDF = ChooseFileWidget('PDF文件路径:   ')
        self.chooseExcel = ChooseFileWidget('excel模板路径: ')
        self.progress = ProgressBarWidget('')

        input_layout = QVBoxLayout()
        input_layout.addWidget(self.choosePDF)
        input_layout.addWidget(self.chooseExcel)
        input_layout.addWidget(self.progress)
        group_box_input = QGroupBox('')
        group_box_input.setLayout(input_layout)

        tab_layout = QVBoxLayout()
        tab_layout.addWidget(group_box_input)
        self.setLayout(tab_layout)

        self.setWindowTitle("View")
        self.resize(500, 250)

        self.choosePDF.button_open.clicked.connect(self.SlotChoosePDF)
        self.chooseExcel.button_open.clicked.connect(self.SlotChooseExcel)
        self.progress.button_start.clicked.connect(self.TraversalFolder)
        self.show()       

    def SlotChoosePDF(self):
        file_path = QFileDialog.getOpenFileName(self)
        self.choosePDF.edit.setText(file_path[0])
        self.signal_PDF = file_path[0]
    
    def SlotChooseExcel(self):
        file_path = QFileDialog.getOpenFileName(self)
        self.chooseExcel.edit.setText(file_path[0])
        self.signal_Excel = file_path[0]

    def ProgressBar(self, check_process):
        self.progress.progress_bar.setValue(check_process)
        QCoreApplication.processEvents()

    def OpenFile(self, filepath):
        try:
            workbook = SharedInfo.excel_app_fg.books.open(filepath, update_links=False)
        except:
            workbook = None
        return workbook

    def CloseFile(self, workbook):
        if workbook != None:
            workbook.close()
        if SharedInfo.excel_app_fg is not None:
            SharedInfo.excel_app_fg.quit()
            SharedInfo.excel_app_fg = None


    def extract_red_differences_and_save_pdf(self, pdf_path, excel_path):
        self.ProgressBar(0)
        if os.path.exists(f'{str(os.path.dirname(app_path()))}\\change_png'):
            shutil.rmtree(f'{str(os.path.dirname(app_path()))}\\change_png')
        os.makedirs(f'{str(os.path.dirname(app_path()))}\\change_png')

        pdf_filename = os.path.basename(pdf_path).replace("(差分原紙)", "")
        excel_filename = pdf_filename.replace(".pdf", "")
        excel_file = self.OpenFile(excel_path)
        sht = excel_file.sheets[0]
        sht.range("B:E").api.NumberFormat = '@'

        # 打开 PDF 文件
        pdf_document = fitz.open(pdf_path)
        output_document = fitz.open()  # 创建一个新的 PDF 文档

        change_no = 0
        for page_num in range(len(pdf_document)):
            page = pdf_document[page_num]
            # page_width = page.rect.width  # 获取页面总宽度
            
            # 将 PDF 页面转换为图像
            pix = page.get_pixmap(dpi=720)
            img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
            img_cv = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
            pix_width = pix.width
            # img.save(f'{str(os.path.dirname(app_path()))}\\change_png\\{page_num}.png')

            # pix2 = page.get_pixmap(dpi=720)
            # img2 = Image.frombytes("RGB", [pix2.width, pix2.height], pix2.samples)

            # 转换为 HSV 颜色空间以检测红色
            hsv = cv2.cvtColor(img_cv, cv2.COLOR_BGR2HSV)
            # 调整颜色范围
            lower_red1 = np.array([0, 50, 50])
            upper_red1 = np.array([10, 255, 255])
            lower_red2 = np.array([170, 50, 50])
            upper_red2 = np.array([180, 255, 255])
            

            # 创建掩码
            mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
            mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
            mask = cv2.bitwise_or(mask1, mask2)


            # 膨胀操作
            kernel = np.ones((12, 12), np.uint8)
            mask = cv2.dilate(mask, kernel, iterations=4)

            # 合并小的轮廓
            mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)  # 闭运算
            
            # 查找红色区域的轮廓
            contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
            
            # 按 page_width / 2 分为左右两部分
            left_contours = []
            right_contours = []
            for contour in contours:
                x, y, w, h = cv2.boundingRect(contour)
                print(x, y, w, h)
                if x <= pix_width / 2:
                    left_contours.append([x, y, w, h])
                    right_contours.append([x+(pix_width/2)-241, y, w, h])
                else:
                    left_contours.append([x-(pix_width/2)+241, y, w, h])
                    right_contours.append([x, y, w, h])


            print(left_contours)
            print(right_contours)
            
            # 按 y 值分组并绘制整体红框
            def draw_grouped_boxes(contours, list_text):
                if not contours:
                    return
                # 按 y 值排序
                contours.sort(key=lambda box: (box[1], box[0], box[3]))
                grouped_boxes = []
                current_group = [contours[0]]

                # print(contours)

                for i in range(1, len(contours)):
                    x1, y1, w1, h1 = current_group[-1]
                    x2, y2, w2, h2 = contours[i]
                    # print(y2, h2)
                    # 如果 y 值重叠或接近，归为一组
                    if abs(y2 - y1) < 10 and abs(x1 - x2) < 150:
                        current_group.append(contours[i])
                    else:
                        grouped_boxes.append(current_group)
                        current_group = [contours[i]]
                grouped_boxes.append(current_group)

                print(grouped_boxes)

                # 绘制每组的整体红框
                for group in grouped_boxes:
                    min_x = min(box[0] for box in group)
                    min_y = min(box[1] for box in group)
                    max_x = max(box[0] + box[2] for box in group)
                    max_y = max(box[1] + box[3] for box in group)
                    # rect = fitz.Rect(min_x, min_y, max_x, max_y)
                    # page.draw_rect(rect, color=(1, 0, 0), width=1)


                    cropped_img = img.crop((min_x, (min_y), max_x, (max_y)))
                    
                    new_width = cropped_img.width // 2
                    new_height = cropped_img.height // 2
                    while new_width/5+10 > 255 or new_height/0.5+10 > 420:
                        new_width = new_width // 2
                        new_height = new_height // 2    
                    cropped_img = cropped_img.resize((new_width, new_height), Image.Resampling.LANCZOS)
                    cropped_img.save(f'{str(os.path.dirname(app_path()))}\\change_png\\change_{page_num}_x_{min_x}_y_{min_y}.png')
                    list_text.append(f'{str(os.path.dirname(app_path()))}\\change_png\\change_{page_num}_x_{min_x}_y_{min_y}.png')

                return grouped_boxes

    
            left_text = [] 
            right_text = []       
            # 绘制左半部分的红框
            left_grouped_boxes = draw_grouped_boxes(left_contours, left_text) 
            # 绘制右半部分的红框
            right_grouped_boxes = draw_grouped_boxes(right_contours, right_text) 

            print(left_text)
            print(right_text)

            self.ProgressBar((page_num+1)/len(pdf_document)*60)

            def draw_comment_box(grouped_boxes, left_text, right_text, str_change, left_right_row):
                write_row = 0
                if grouped_boxes is None:
                    return write_row
                
                for i in range(0, len(grouped_boxes)):
                    min_x = min(box[0] for box in grouped_boxes[i])
                    min_y = min(box[1] for box in grouped_boxes[i])
                    max_x = max(box[0] + box[2] for box in grouped_boxes[i])
                    max_y = max(box[1] + box[3] for box in grouped_boxes[i])

                    fill_color = compare_images(left_text[i], right_text[i])
                    if fill_color is None:
                        continue

                    # 动态计算备注框的宽度
                    note_text = f"{(change_no+write_row):03}"
                    font_size = 8
                    text_width = fitz.get_text_length(note_text, fontsize=font_size)
                    note_rect_width = text_width + 10  # 确保备注框至少与红框等宽
                    
                    # 添加备注框
                    note_rect = fitz.Rect(min_x/10+5, max_y/10+3, min_x/10+5+note_rect_width, max_y/10+18)  # 备注框位置在红框下方
                    page.draw_rect(note_rect, color=(1, 0, 0), fill=fill_color, width=0.5)  # 填充颜色
                    page.insert_text(note_rect.top_left + (5, 10), note_text, fontsize=font_size, color=(0, 0, 0))  # 添加备注文字

                    # 写入excel
                    temp_img_path = f'{str(os.path.dirname(app_path()))}\\change_png\\change_{page_num}_x_{min_x}_y_{min_y}.png'
                    rows = 3+(change_no+write_row)*2
                    if left_right_row == 0:
                        sht.range(f'B{rows}').value = note_text
                        sht.range(f'B{rows}:B{rows+1}').merge()
                        sht.range(f'D{rows}:D{rows+1}').merge()
                        sht.range(f'E{rows}:E{rows+1}').merge()

                    cell_address = f'C{3+(change_no+write_row)*2+left_right_row}'
                    sht.range(cell_address).value = f'{str_change}{chr(10)}'
                    picture = sht.pictures.add(temp_img_path, left=sht.range(cell_address).left, top=sht.range(cell_address).top)
                    cell_width = picture.width / 5 + 5  # 将图片宽度转换为列宽（1列宽约等于7.5像素）
                    cell_height = picture.height / 0.5 + 5  # 将图片高度转换为行高（1行高约等于0.75像素）
                    print(picture.width)
                    print(picture.height)
                    
                    # print(sht.range(cell_address).column_width)
                    sht.range(cell_address).column_width = max(sht.range(cell_address).column_width, cell_width)
                    sht.range(cell_address).row_height = max(sht.range(cell_address).row_height, cell_height)

                    picture.api.ShapeRange.IncrementLeft(10)
                    picture.api.ShapeRange.IncrementTop(20)

                    write_row += 1

                return write_row


            write_row = draw_comment_box(left_grouped_boxes, left_text, right_text, "■変更前：", 0)
            write_row = draw_comment_box(right_grouped_boxes, left_text, right_text, "■変更後：", 1)
            change_no += write_row

            # 将修改后的页面添加到输出 PDF
            output_document.insert_page(-1, text="", width=page.rect.width, height=page.rect.height)
            output_document[-1].show_pdf_page(page.rect, pdf_document, page_num)

        self.ProgressBar(80)
        
        # 保存输出 PDF
        output_pdf_path = f'{str(os.path.dirname(app_path()))}\\(差分結果){pdf_filename}'
        output_document.save(output_pdf_path)
        pdf_document.close()
        output_document.close()

        row_start = 3
        row_end = sht.used_range.last_cell.row
        selection = sht.range(f'B{row_start}:E{row_end}')

        for edge in [constants.xlEdgeLeft, constants.xlEdgeTop, constants.xlEdgeBottom, constants.xlEdgeRight, constants.xlInsideVertical, constants.xlInsideHorizontal]:
            selection.api.Borders(edge).LineStyle = constants.xlContinuous
            selection.api.Borders(edge).Weight = constants.xlThin
        selection.api.Font.Bold = False
        # 保存输出 excel
        output_excel_path = f'{str(os.path.dirname(app_path()))}\\{excel_filename}仕様差分の通し番号一覧.xlsx'
        excel_file.save(output_excel_path)
        self.CloseFile(excel_file)

        if os.path.exists(f'{str(os.path.dirname(app_path()))}\\change_png'):
            shutil.rmtree(f'{str(os.path.dirname(app_path()))}\\change_png')

        self.ProgressBar(100)



    def TraversalFolder(self): # start
        PDF_path = self.signal_PDF
        excel_path = self.signal_Excel
        if re.search('[\u4e00-\u9fff]', str(os.path.dirname(app_path()))):
            QMessageBox.warning(None, 'warning', '工具路径只能有英文           ')
            return
        if 'PySide6.QtCore' in str(PDF_path) or str(PDF_path) == '':
            QMessageBox.warning(None, 'warning', '请选择PDF文件路径           ')
            return
        if not '.pdf' in str(PDF_path):
            QMessageBox.warning(None, 'warning', '选择的文件不是PDF文件           ')
            return
        if 'PySide6.QtCore' in str(excel_path) or str(excel_path) == '':
            QMessageBox.warning(None, 'warning', '请选择Excel模板路径           ')
            return
        if not '(template)仕様差分の通し番号一覧.xlsx' in str(excel_path):
            QMessageBox.warning(None, 'warning', '选择的excel不是模板文件           ')
            return
        
        SharedInfo.excel_app_fg = PreloadExcelRelatedHidden()
        self.extract_red_differences_and_save_pdf(PDF_path, excel_path)

def compare_images(image_a_path, image_b_path):
    # 加载图像
    img_a = cv2.imread(image_a_path)
    img_b = cv2.imread(image_b_path)

    # # 调整图像大小为相同
    # if img_a.shape != img_b.shape:
    #     img_b = cv2.resize(img_b, (img_a.shape[1], img_a.shape[0]))

    # 转换为灰度图像
    gray_a = cv2.cvtColor(img_a, cv2.COLOR_BGR2GRAY)
    gray_b = cv2.cvtColor(img_b, cv2.COLOR_BGR2GRAY)

    # 计算像素差异
    diff = gray_b.astype(np.int16) - gray_a.astype(np.int16)  # 使用 int16 防止溢出
    total_pixels = diff.size


    # 统计颜色变化的像素点
    darker_pixels = np.sum(diff < -10)  # 颜色变深的像素点
    lighter_pixels = np.sum(diff > 10)  # 颜色变浅的像素点
    unchanged_pixels = np.sum(np.abs(diff) <= 10)  # 不变的像素点

    # 计算比例
    darker_ratio = darker_pixels / (total_pixels - unchanged_pixels)
    lighter_ratio = lighter_pixels / (total_pixels - unchanged_pixels)
    unchanged_ratio = unchanged_pixels / total_pixels

    print(darker_ratio)
    print(lighter_ratio)
    print(unchanged_ratio)

    # 判断变化类型
    if darker_ratio > 0.95:
        color = (1, 1, 0)
    elif lighter_ratio > 0.95:
        color = (0.5, 0.5, 0.5)
    elif unchanged_ratio > 0.95:
        color = None    
    else:
        color = (0, 1, 0)
    return color



class SharedInfo:
    excel_app_fg = None
    workbook = None

def PreloadExcelRelatedHidden():
    excel_app_fg = xw.App(visible=False,add_book=False)
    excel_app_fg.Visible = False
    excel_app_fg.Interactive = False
    excel_app_fg.DisplayAlerts = False
    excel_app_fg.ScreenUpdating = False
    return excel_app_fg


if __name__ == '__main__':
    startapp = QApplication([])
    window = FileCheckView()
    startapp.exec()

